#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "sprout_util.h"
#include "sprout.h"

/**
 * 获取字符串长度, 空指针为 0
 *
 * @param s 字符串
 * @return 字符串长度
 */
static size_t str_len(const char *s);

/**
 * 字符串拼接, 拼接完成后释放拼接的字符串
 *
 * @param dest 目标字符串
 * @param src 拼接的字符串
 */
static void str_cat_free(char *dest, char *src);

void *allocate_memory(size_t sz)
{
    void *result = malloc(sz);
    memset(result, 0, sz);
    return result;
}

void free_memory(void *p)
{
    if (p)
    {
        free(p);
    }
}

inline void skip_whitespace(const char **p)
{
    while (is_whitespace(**p))
    {
        (*p)++;
    }
}

Result *hex_str_to_int(const char *str)
{
    Result *res = (Result *)allocate_memory(sizeof(Result));
    if (str_len(str) < 4)
    {
        goto error;
    }
    char buf[5] = {str[0], str[1], str[2], str[3], '\0'};
    char *end;
    unsigned long ul = strtoul(buf, &end, 16);
    if (end != buf + 4)
    {
        goto error;
    }
    unsigned int *p = (unsigned int *)allocate_memory(sizeof(unsigned int));
    *p = (unsigned int)ul;
    res->value = p;
    return res;
error:
    res->is_error = true;
    res->msg = "十六进制数字转换失败";
    return res;
}

Object *json_to_c(const char **js, const char start, const char end)
{
    skip_whitespace(js);
    if (**js == start)
    {
        Value *value = json_values_to_c(js, start == LEFT_BRACE && end == RIGHT_BRACE);
        if (**js == end)
        {
            Object *obj = (Object *)allocate_memory(sizeof(Object));
            obj->value = value;
            return obj;
        }
        free_value(value);
    }
    return NULL;
}

Value *json_values_to_c(const char **js, bool is_obj)
{
    Value *result = NULL;
    Value *curr;
    Value *pre = NULL;
    char *k = NULL;
    do
    {
        (*js)++;
        if (is_obj)
        {
            // todo: 解析 k
        }
        curr = json_value_to_c(js);
        if (!curr)
        {
            return result;
        }
        curr->k = k;
        if (pre)
        {
            pre->next = curr;
        }
        if (!result)
        {
            result = curr;
        }
        pre = curr;
        (*js)++;
        skip_whitespace(js);
    } while (**js == COMMA);
    return result;
}

Value *json_value_to_c(const char **js)
{
    skip_whitespace(js);
    Value *value = (Value *)allocate_memory(sizeof(Value));
    switch (**js)
    {
    case LEFT_BRACE:
        value->v = json_object_to_c_object(js);
        value->t = J_OBJECT;
        return value;
    case LEFT_BRACKET:
        value->v = json_array_to_c_array(js);
        value->t = J_ARRAY;
        return value;
    }
    free_value(value);
    return NULL;
}

char *c_to_json(const Object *obj, const char start, const char end)
{
    if (!obj)
    {
        return NULL;
    }
    char *value = c_values_to_json(obj->value, start == LEFT_BRACE && end == RIGHT_BRACE);
    size_t value_len = str_len(value);
    char *result = (char *)allocate_memory(1 + value_len + 1 + 1);
    result[0] = start;
    str_cat_free(result, value);
    result[value_len + 1] = end;
    return result;
}

char *c_values_to_json(const Value *v, bool is_obj)
{
    if (!v)
    {
        return NULL;
    }
    const Value *curr = v;
    char *k = NULL;
    char *result = NULL;
    size_t result_len = 0;
    size_t k_len = 0;
    do
    {
        char *value = c_value_to_json(curr);
        size_t value_len = str_len(value);
        if (is_obj && value)
        {
            // todo: 解析 k
        }
        size_t curr_len = result_len + k_len + value_len;
        if (result)
        {
            curr_len++;
        }
        char *curr_result = (char *)allocate_memory(curr_len + 1);
        str_cat_free(curr_result, result);
        if (value && result)
        {
            curr_result[result_len] = COMMA;
        }
        if (value)
        {
            str_cat_free(curr_result, k);
        }
        str_cat_free(curr_result, value);
        result = curr_result;
        result_len = curr_len;
        curr = curr->next;
    } while (curr);
    return result;
}

char *c_value_to_json(const Value *v)
{
    if (!v)
    {
        return NULL;
    }
    switch (v->t)
    {
    case J_OBJECT:
        return c_object_to_json_object((Object *)v->v);
    case J_ARRAY:
        return c_array_to_json_array((Array *)v->v);
    }
}

void free_result(Result *res)
{
    if (res)
    {
        free_memory(res->value);
        free_memory(res);
    }
}

TO_TYPE(to_u_int, TYPE_U_INT, unsigned int)

static size_t str_len(const char *s)
{
    if (!s)
    {
        return 0L;
    }
    return strlen(s);
}

static void str_cat_free(char *dest, char *src)
{
    if (!dest || !src)
    {
        return;
    }
    strcat(dest, src);
    free_memory(src);
}
